#include "backdrop.h"
#include "decal.h"
#include "backdrop_editor.h"
#include "backdrop_sfx.h"
#include "transient_backdrop.h"
#include "transient_decal.h"

#include "graphics/image.h"
#include "graphics/graphics.h"
#include "graphics/graphics_util.h"

#include "util/timer.h"		    // Needs to come in early
#include "util/mmgr.h"		    // Needs to come in early
#include "util/common_macros.h"		// macros
#include "util/log.h"		    // logging

using namespace ui;
using namespace script;
using namespace backdrop;

Backdrop::Backdrop(camera::FbCamera* pCamera) :
	m_pCamera(pCamera),
	m_pEditor(NULL),
	m_pSFX(NULL),
	m_pTransient(NULL),
	m_Filename(""),
	m_Offset(0.0f, 0.0f),
	m_WindSpeed(0.0f, 0.0f),
	m_Gravity(0.0f, 0.0f)
{
}

Backdrop::~Backdrop()
{
	Clear();
	UTIL_SAFE_DELETE(m_pTransient);
}

void Backdrop::SetBackgroundImg(const std::string& File)
{
	m_BackgroundImg.Create(File);

	if (m_BackgroundImg.GetImage())
	{
		// loaded PNG or JPG manually - UV not automatically set up
		m_BackgroundImg.m_UV1 = math::Vec2(0.0f, 0.0f);
		m_BackgroundImg.m_UV2 = math::Vec2(1.0f, 1.0f);

		if (m_BackgroundImg.GetImage()->GetWidth() > GetGraphics()->GetScreenWidth())
			m_BackgroundImg.m_UV2.x = float(GetGraphics()->GetScreenWidth()) / float(m_BackgroundImg.GetImage()->GetWidth());

		if (m_BackgroundImg.GetImage()->GetHeight() > GetGraphics()->GetScreenHeight())
			m_BackgroundImg.m_UV2.y = float(GetGraphics()->GetScreenHeight()) / float(m_BackgroundImg.GetImage()->GetHeight());
	}

	m_BackgroundImg.Setup(math::Vec2(0.0f, 0.0f), math::Vec2(1.0f, 1.0f));
}

void Backdrop::AddDecalSprite(Decal* pDecal)
{
	std::vector<Decal*>* pList = pDecal->IsForeground() ? &m_ForeDecals : &m_BackDecals;

	for (unsigned int i = 0; i < pList->size(); i++)
	{
		if ((*pList)[i]->GetDecalType() > pDecal->GetDecalType()) {
			pList->insert(pList->begin() + i, pDecal);
			return;
		}
	}

	pList->push_back(pDecal);
}

Decal* Backdrop::GetDecalSprite(const math::Vec2& Pos)
{
	unsigned int i;

	for (i = (unsigned int)m_ForeDecals.size(); i > 0; i--)
		if (m_ForeDecals[i-1]->HitTest(Pos)) return m_ForeDecals[i-1];

	for (i = (unsigned int)m_BackDecals.size(); i > 0; i--)
		if (m_BackDecals[i-1]->HitTest(Pos)) return m_BackDecals[i-1];

	return NULL;
}

bool Backdrop::DeleteDecalSprite(Decal* pDecal)
{
	unsigned int i;

	for (i = 0; i < m_ForeDecals.size(); i++)
	{
		if (pDecal == m_ForeDecals[i]) {
			m_ForeDecals.erase(m_ForeDecals.begin() + i);
			UTIL_SAFE_DELETE(pDecal);
			return true;
		}
	}

	for (i = 0; i < m_BackDecals.size(); i++)
	{
		if (pDecal == m_BackDecals[i]) {
			m_BackDecals.erase(m_BackDecals.begin() + i);
			UTIL_SAFE_DELETE(pDecal);
			return true;
		}
	}

	return false;
}

void Backdrop::ResetDecalPos()
{
	unsigned int i;
	math::Vec2 Pos;

	for (i = 0; i < m_ForeDecals.size(); i++) {
		Pos = m_ForeDecals[i]->GetStartPos();
		m_ForeDecals[i]->SetPos(Pos);
	}

	for (i = 0; i < m_BackDecals.size(); i++) {
		Pos = m_BackDecals[i]->GetStartPos();
		m_BackDecals[i]->SetPos(Pos);
	}

	if (m_pTransient) m_pTransient->ResetDecals();
}

void Backdrop::AddTransientSprite(const std::string& File, const math::Vec2& pos, const math::Vec2& dir, int Layer)
{
	if (!m_pTransient) return;

	m_pTransient->AddDecal(File, pos, dir, Layer);
}

TransientDecal* Backdrop::GetTransientSprite(const math::Vec2& Pos)
{
	if (!m_pTransient) return NULL;

	return m_pTransient->GetDecal(Pos);
}

void Backdrop::DeleteTransientSprite(TransientDecal* pDecal, bool bDelete)
{
	if (!m_pTransient) return;

	m_pTransient->DropDecal(pDecal, bDelete);
}

void Backdrop::Render()
{
	if (m_BackgroundImg.IsValid()) m_BackgroundImg.Render();
	if (m_pTransient) m_pTransient->Render();

	PreRender();
	PostRender();
}

void Backdrop::PreRender()
{
	bool bEnableSnap = GraphicsUtil::SnapToPixel;

	for (unsigned int i = 0; i < m_BackDecals.size(); i++) {
		GraphicsUtil::SnapToPixel = !m_BackDecals[i]->AffectedByWind();
		m_BackDecals[i]->Render();
	}

	GraphicsUtil::SnapToPixel = bEnableSnap;
}

void Backdrop::PostRender()
{
	bool bEnableSnap = GraphicsUtil::SnapToPixel;

	for (unsigned int i = 0; i < m_ForeDecals.size(); i++) {
		GraphicsUtil::SnapToPixel = !m_ForeDecals[i]->AffectedByWind();
		m_ForeDecals[i]->Render();
	}

	GraphicsUtil::SnapToPixel = bEnableSnap;

	if (m_pEditor) m_pEditor->Render();
}

void Backdrop::Tick(float dt)
{
	unsigned int i;
	math::Vec2 move;

	if (m_BackgroundImg.IsValid()) m_BackgroundImg.Tick(dt);
	if (m_pTransient) m_pTransient->Tick(dt, GetEditor() ? false : true);

	for (i = 0; i < m_BackDecals.size(); i++) {
		m_BackDecals[i]->Tick(dt);
		if (!m_pEditor && m_BackDecals[i]->AffectedByWind()) m_BackDecals[i]->Move(m_WindSpeed * dt);
	}

	for (i = 0; i < m_ForeDecals.size(); i++) {
		m_ForeDecals[i]->Tick(dt);
		if (!m_pEditor && m_ForeDecals[i]->AffectedByWind()) m_ForeDecals[i]->Move(m_WindSpeed * dt);
	}

	if (m_pEditor) m_pEditor->Tick(dt);
	if (m_pSFX) m_pSFX->Tick(dt);
}

void Backdrop::Clear()
{
	ClearLevel();

	UTIL_SAFE_DELETE(m_pEditor);
	UTIL_SAFE_DELETE(m_pSFX);
}

void Backdrop::ClearLevel()
{
	unsigned int i;

	for (i = 0; i < m_BackDecals.size(); i++)
		UTIL_SAFE_DELETE(m_BackDecals[i]);

	for (i = 0; i < m_ForeDecals.size(); i++)
		UTIL_SAFE_DELETE(m_ForeDecals[i]);

	m_BackDecals.clear();
	m_ForeDecals.clear();

	if (m_pTransient) m_pTransient->Clear();
}

bool Backdrop::LoadBackdrop(const std::string& File)
{
	Clear();
	m_Filename = File;

	unsigned int i, count;

	LuaPlus::LuaState* pScript = ScriptHelper::CreateLuaState();

	if (!ScriptHelper::DoFile(*pScript, File))
	{
		_LOG(MSG_ERROR, "Unable to load backdrop from file " << File << "!");
		LuaPlus::LuaState::Destroy(pScript);
		return false;
	}
	else
	{
		LuaPlus::LuaObject Data, SubData; // declare HERE so that it goes out of scope before Destroy() is called

		// background decals
		Data = ScriptHelper::GetObj(*pScript, "Decals");

		if (Data.IsTable())
		{
			count = Data.GetTableCount();
			for (i = 0; i < count; i++)
			{
				SubData = Data.GetByIndex(i + 1);

				std::string ImageTxt = ScriptHelper::GetTableString(SubData, "Image", "");
				float x = ScriptHelper::GetTableFloat(SubData, "X", 0.0f);
				float y = ScriptHelper::GetTableFloat(SubData, "Y", 0.0f);

				if (!ImageTxt.empty()) {
					if (Decal* pDecal = CreateDecalSprite(ImageTxt, math::Vec2(x, y)))
						AddDecalSprite(pDecal);
				}
			}
		}

		// background & environment
		Data = ScriptHelper::GetObj(*pScript, "Environment");

		if (Data.IsTable())
		{
			std::string ImageTxt = ScriptHelper::GetTableString(Data, "Background", "");
			m_WindSpeed.x = ScriptHelper::GetTableFloat(Data, "WindX", 0.0f);
			m_WindSpeed.y = ScriptHelper::GetTableFloat(Data, "WindY", 0.0f);
			m_Gravity.y = ScriptHelper::GetTableFloat(Data, "Gravity", 0.0f);

			if (!ImageTxt.empty()) SetBackgroundImg(ImageTxt);
		}

		// transient objects
		if (m_pTransient) m_pTransient->LoadTable(pScript);

		// special
		LoadSpecial(pScript);
	}

	LuaPlus::LuaState::Destroy(pScript);
	return true;
}

bool Backdrop::SaveBackdrop(const std::string& File)
{
	WriteLua Output;
	unsigned int i;

	if (!Output.Open(File)) return false;
	m_Filename = File;

	Output.StartTable("Decals");
	for (i = 0; i < m_BackDecals.size(); i++)
	{
		Output.StartTable();

		Output.WritePair("Image", m_BackDecals[i]->GetTypeText());
		Output.WritePair("X", m_BackDecals[i]->GetStartPos().x);
		Output.WritePair("Y", m_BackDecals[i]->GetStartPos().y);
		Output.CloseTable();
	}
	for (i = 0; i < m_ForeDecals.size(); i++)
	{
		Output.StartTable();

		Output.WritePair("Image", m_ForeDecals[i]->GetTypeText());
		Output.WritePair("X", m_ForeDecals[i]->GetStartPos().x);
		Output.WritePair("Y", m_ForeDecals[i]->GetStartPos().y);
		Output.CloseTable();
	}
	Output.CloseTable(false);

	Output.StartTable("Environment");
	Output.WritePair("Background", m_BackgroundImg.GetFilename());
	Output.WritePair("WindX", m_WindSpeed.x);
	Output.WritePair("WindY", m_WindSpeed.y);
	Output.WritePair("Gravity", m_Gravity.y);
	Output.CloseTable(false);

	if (m_pTransient) m_pTransient->SaveTable(Output);

	SaveSpecial(Output);
	return true;
}
